﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Common;
using System.Reflection;
using System.Reflection.Emit;
using System.Collections;
using DTOFrameworks;
using FooFun.DataElements.Dtos;

namespace PhysicalDataProvider
{
    public class DataReaderEmit
    {
        static DataReaderEmit()
        {
            AssemblyName aName
                    = new AssemblyName("DynamicDTOAssembly");

            AssemblyBuilder ab =
                AppDomain.CurrentDomain.DefineDynamicAssembly(
                    aName,
                    AssemblyBuilderAccess.RunAndSave);

            // For a single-module assembly, the module name is usually
            // the assembly name plus an extension.
            mb =
                ab.DefineDynamicModule(aName.Name, aName.Name + ".dll");
        }

        static ModuleBuilder mb;

        public static IDataElement GetDataElement
            (DbDataReader reader, string typeName)
        {
            Type createdType = mb.GetType(typeName);

            if (createdType == null)
            {
                #region 构建类型
                TypeBuilder typeBuilder = mb.DefineType(
                    typeName,
                    TypeAttributes.Public);

                for (int i = 0; i < reader.FieldCount; i++)
                {
                    string propertyName = reader.GetName(i);
                    string fieldName = "_" + propertyName;
                    string getMethodName = "get_" + propertyName;
                    string setMethodName = "set_" + propertyName;

                    Type type = reader.GetFieldType(i);

                    FieldBuilder fieldBuilder
                        = typeBuilder.DefineField(fieldName,
                                        type,
                                        FieldAttributes.Private);

                    PropertyBuilder propertyBuilder
                        = typeBuilder.DefineProperty(
                            propertyName,
                            PropertyAttributes.None,
                            type,
                            null);

                    // The property set and property get methods require a special
                    // set of attributes.
                    MethodAttributes getSetAttr =
                        MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;

                    // Define the "get" accessor method for CustomerName.
                    MethodBuilder getMethodBulider =
                        typeBuilder.DefineMethod(getMethodName,
                                                   getSetAttr,
                                                   type,
                                                   Type.EmptyTypes);

                    ILGenerator getIL = getMethodBulider.GetILGenerator();

                    getIL.Emit(OpCodes.Ldarg_0);
                    getIL.Emit(OpCodes.Ldfld, fieldBuilder);
                    getIL.Emit(OpCodes.Ret);

                    MethodBuilder setMethodBulider =
        typeBuilder.DefineMethod(setMethodName,
                                   getSetAttr,
                                   null,
                                   new Type[] { type });

                    ILGenerator setIL = setMethodBulider.GetILGenerator();

                    setIL.Emit(OpCodes.Ldarg_0);
                    setIL.Emit(OpCodes.Ldarg_1);
                    setIL.Emit(OpCodes.Stfld, fieldBuilder);
                    setIL.Emit(OpCodes.Ret);

                    propertyBuilder.SetGetMethod(getMethodBulider);
                    propertyBuilder.SetSetMethod(setMethodBulider);

                }

                createdType = typeBuilder.CreateType();

                #endregion
            }

            ArrayList list = new ArrayList();
            ObjectDataElement objDE = null;
            while (reader.Read())
            {
                object obj = Activator.CreateInstance(createdType);

                objDE = new ObjectDataElement(obj);

                for (int i = 0; i < reader.FieldCount; i++)
                {
                    string propertyName = reader.GetName(i);


                    objDE[propertyName]
                        = new ObjectDataElement(reader[i]);
                }

                list.Add(obj);
            }

            if (list.Count == 0)
            {
                return new EmptyDataElement();
            }
            else if (list.Count == 1)
            {
                return objDE;
            }

            return new ListDataElement(list);
        }

        public static IDataElement GetDataElement(DbDataReader dr, string[] cols, string table)
        {
            string typeName = table + "_" + string.Join("_", cols);

            return GetDataElement(dr, typeName);
        }
    }
}
